<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<meta name="keywords" content="nacos" />
	<meta name="description" content="Nacos 配置管理帮我们解决什么问题？" />
	<!-- 网页标签标题 -->
	<title>Nacos 帮我们解决什么问题？—— 配置管理篇</title>
	<link rel="shortcut icon" href="https://img.alicdn.com/tfs/TB1hgJpHAPoK1RjSZKbXXX1IXXa-64-64.png"/>
	<link rel="stylesheet" href="/build/blogDetail.css" />
</head>
<body>
	<div id="root"><div class="blog-detail-page" data-reactroot="" data-reactid="1" data-react-checksum="1010227366"><header class="header-container header-container-normal" data-reactid="2"><div class="header-body" data-reactid="3"><a href="/zh-cn/index.html" data-reactid="4"><img class="logo" src="/img/nacos_colorful.png" data-reactid="5"/></a><div class="search search-normal" data-reactid="6"><span class="icon-search" data-reactid="7"></span></div><span class="language-switch language-switch-normal" data-reactid="8">En</span><div class="header-menu" data-reactid="9"><img class="header-menu-toggle" src="/img/menu_gray.png" data-reactid="10"/><ul data-reactid="11"><li class="menu-item menu-item-normal" data-reactid="12"><a href="/zh-cn/index.html" data-reactid="13">首页</a></li><li class="menu-item menu-item-normal" data-reactid="14"><a href="/zh-cn/docs/what-is-nacos.html" data-reactid="15">文档</a></li><li class="menu-item menu-item-normal menu-item-normal-active" data-reactid="16"><a href="/zh-cn/blog/index.html" data-reactid="17">博客</a></li><li class="menu-item menu-item-normal" data-reactid="18"><a href="/zh-cn/community/index.html" data-reactid="19">社区</a></li><li class="menu-item menu-item-normal" data-reactid="20"><a href="https://cn.aliyun.com/product/aliware/mse?spm=nacos-website.topbar.0.0.0" data-reactid="21">企业版Nacos</a><img class="menu-img" src="https://img.alicdn.com/tfs/TB1esl_m.T1gK0jSZFrXXcNCXXa-200-200.png" data-reactid="22"/></li><li class="menu-item menu-item-normal" data-reactid="23"><a href="https://job.alibaba.com/zhaopin/position_detail.htm?trace=qrcode_share&amp;positionCode=GP633620" data-reactid="24">招贤纳士</a><img class="menu-img" src="https://img.alicdn.com/tfs/TB1esl_m.T1gK0jSZFrXXcNCXXa-200-200.png" data-reactid="25"/></li><li class="menu-item menu-item-normal" data-reactid="26"><a href="http://console.nacos.io/nacos/index.html" data-reactid="27">控制台样例</a></li></ul></div></div></header><section class="blog-content markdown-body" data-reactid="28"><h1>Nacos 帮我们解决什么问题？—— 配置管理篇</h1>
<blockquote>
<p>Authors: 何煦</p>
</blockquote>
<h2>概述</h2>
<p><a href="https://github.com/alibaba/nacos">Nacos</a> 是阿里巴巴今年7月份开源的项目，如其名， Naming and Configuration Service ，专注于服务发现和配置管理领域。本系列文章，将从 5W1H（What、Where、When、Who、Why、How）全面剖析 Nacos，希望对开发者们在服务发现和配置管理开源方案选型的时候，有所帮助。</p>
<p>本文作为 Nacos 系列文章的开篇，从 “What” 开始。我们开始关注一个开源项目的时候，通常最先冒出的 2 个问题是：</p>
<ul>
<li>它是什么？</li>
<li>它帮我们解决什么问题？</li>
</ul>
<p>Nacos 是什么？</p>
<p>Nacos 是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。</p>
<p>Nacos 能帮我们解决什么问题？</p>
<p>本文将先围绕其“配置管理”功能来解答。配置，作为代码如影随形的小伙伴，伴随着应用的整个生命周期，我们当然对它也非常的熟悉，想想配置一般都通过哪几种形式存在？</p>
<ul>
<li>硬编码</li>
<li>配置文件</li>
<li>DB 配置表</li>
</ul>
<h2>硬编码</h2>
<p>配置项作为类字段的形式存在，如：</p>
<pre><code class="language-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppConfig</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> connectTimeoutInMills = <span class="hljs-number">5000</span>;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getConnectTimeoutInMills</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> connectTimeoutInMills;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setConnectTimeoutInMills</span><span class="hljs-params">(<span class="hljs-keyword">int</span> connectTimeoutInMills)</span> </span>{
        <span class="hljs-keyword">this</span>.connectTimeoutInMills = connectTimeoutInMills;
    }
}
</code></pre>
<p>这种形式主要有三个问题：</p>
<p>如果配置是需要动态修改的话，需要当前应用去暴露管理该配置项的接口，至于是 Controller 的 API 接口，还是 JMX ，都是可以做到。</p>
<p>另外，配置变更都是发生在内存中，并没有持久化。因此，在修改配置之后重启应用，配置又会变回代码中的默认值了，这是一个坑啊，笔者就曾经掉进去过，爬了好一会才上岸。</p>
<p>最后一个问题，就是当你有多台机器的时候，要修改一个配置，每一台都得去操作一遍，运维成本可想而知，极其蛋疼。</p>
<h2>配置文件</h2>
<p>Spring 中常见的 properties、yml 文件，或其他自定义的，如，“conf”后缀等：</p>
<pre><code># application.properties
connectTimeoutInMills=5000
</code></pre>
<p>相比“硬编码”的形式，它解决了第二个问题，持久化了配置。但是，另外两个问题并没有解决，运维成本依旧还是很高的。</p>
<p>配置动态变更，可以是通过类似“硬编码”暴露管理接口的方式，这时，代码中会多一步持久化新配置到文件的逻辑。或者，简单粗暴点，直接登录机器上去修改配置文件，再重启应用，让配置生效。当然，你也可以在代码中增加一个定时任务，如每隔 10s 读取配置文件内容，让最新的配置能够及时在应用中生效，这样也就免去了重启应用这个“较重”的运维操作。</p>
<p>通过增加“持久化逻辑”、“定时任务”让“配置文件”的形式比“硬编码”前进了一小步。</p>
<h2>DB 配置表</h2>
<p>这里的 DB 可以是 MySQL 等的关系型数据库，也可以是 Redis 等的非关系型数据库。数据表如：</p>
<pre><code class="language-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-string">`config`</span> (
  <span class="hljs-string">`id`</span> <span class="hljs-built_in">bigint</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> AUTO_INCREMENT,
  <span class="hljs-string">`key`</span> <span class="hljs-built_in">varchar</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">''</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'配置项'</span>,
  <span class="hljs-string">`value`</span> <span class="hljs-built_in">varchar</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-string">''</span> <span class="hljs-keyword">COMMENT</span> <span class="hljs-string">'配置内容'</span>,
  <span class="hljs-string">`updated_time`</span> <span class="hljs-keyword">timestamp</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
  <span class="hljs-string">`created_time`</span> <span class="hljs-keyword">timestamp</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CURRENT_TIMESTAMP</span>,
  PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-string">`id`</span>),
  <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">KEY</span> <span class="hljs-string">`idx_key`</span> (<span class="hljs-string">`key`</span>)
) <span class="hljs-keyword">ENGINE</span>=<span class="hljs-keyword">InnoDB</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CHARSET</span>=utf8 <span class="hljs-keyword">COMMENT</span>=<span class="hljs-string">'配置信息'</span>;

<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-string">`config`</span> (<span class="hljs-string">`key`</span>, <span class="hljs-string">`value`</span>, <span class="hljs-string">`updated_time`</span>, <span class="hljs-string">`created_time`</span>) <span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'connectTimeoutInMills'</span>, <span class="hljs-string">'5000'</span>, <span class="hljs-keyword">CURRENT_TIMESTAMP</span>, <span class="hljs-keyword">CURRENT_TIMESTAMP</span>);
</code></pre>
<p>它相对于前两者，更进一步，将配置从应用中抽离出来，集中管理，能较大的降低运维成本。</p>
<p>那么，它能怎么解决动态更新配置的问题呢？据我所知，有两种方式。</p>
<p>其一，如同之前一样，通过暴露管理接口去解决，当然，也一样得增加持久化的逻辑，只不过，之前是写文件，现在是将最新配置写入数据库。不过，程序中还需要有定时从数据库读取最新配置的任务，这样，才能做到只需调用其中一台机器的管理配置接口，就能把最新的配置下发到整个应用集群所有的机器上，真正达到降低运维成本的目的。</p>
<p>其二，直接修改数据库，程序中通过定时任务从数据库读取最新的配置内容。</p>
<p>“DB 配置表”的形式解决了主要的问题，但是它不够优雅，带来了一些“累赘”。</p>
<h2>Nacos 配置管理</h2>
<p>Nacos 真正将配置从应用中剥离出来，统一管理，优雅的解决了配置的动态变更、持久化、运维成本等问题。</p>
<p>应用自身既不需要去添加管理配置接口，也不需要自己去实现配置的持久化，更不需要引入“定时任务”以便降低运维成本。Nacos 提供的配置管理功能，将配置相关的所有逻辑都收拢，并且提供简单易用的 SDK，让应用的配置可以非常方便被 Nacos 管理起来。</p>
<p>如果是在 Spring 中使用 Nacos，只需三个步骤即可：</p>
<ol>
<li>添加依赖</li>
</ol>
<pre><code class="language-plain">&lt;dependency&gt;
    &lt;groupId&gt;com.alibaba.nacos&lt;/groupId&gt;
    &lt;artifactId&gt;nacos-spring-context&lt;/artifactId&gt;
    &lt;version&gt;${latest.version}&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<ol start="2">
<li>添加 <code>@EnableNacosConfig</code> 注解启用 Nacos Spring 的配置管理服务。以下示例中，我们使用 <code>@NacosPropertySource</code> 加载了 <code>dataId</code> 为 <code>example</code> 的配置源，并开启自动更新：</li>
</ol>
<pre><code class="language-plain">@Configuration
@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = &quot;127.0.0.1:8848&quot;))
@NacosPropertySource(dataId = &quot;example&quot;, autoRefreshed = true)
public class NacosConfiguration {

}
</code></pre>
<ol start="3">
<li>通过 Spring 的 <code>@Value</code> 注解设置属性值。</li>
</ol>
<p>注意：需要同时有 <code>Setter</code>方法才能在配置变更的时候自动更新。</p>
<pre><code class="language-plain">public class AppConfig {

    @Value(&quot;${connectTimeoutInMills:5000}&quot;)
    private int connectTimeoutInMills;

    public int getConnectTimeoutInMills() {
        return connectTimeoutInMills;
    }

    public void setConnectTimeoutInMills(int connectTimeoutInMills) {
        this.connectTimeoutInMills = connectTimeoutInMills;
    }
}
</code></pre>
<p>以上的三个步骤，对应用本身几乎没有任何的侵入，1 个依赖 2 注解，寥寥数行，就把配置通过 Nacos 管理起来了。</p>
<p>关于配置的动态更新，对 Nacos Spring 的用户来说，在自身应用中就只是设置 “autoRefreshed” 的一个布尔值。然后在需要修改配置的时候，调用 Nacos 修改配置的接口，或使用 Nacos 的控制台去修改，配置发生变更后， Nacos 就会把最新的配置推送到该应用的所有机器上，简单而高效。</p>
<p>想想之前，为了实现此功能，写了多少冤枉代码，做了多少冤枉的运维工作。要是早一点认识 Nacos，该有多好呀！</p>
<h2>总结</h2>
<p>本文作为 Nacos 5W1H 系列文章的开篇，从“What” 讲述了 Nacos 配置管理能帮我们解决的问题：以简单、优雅、高效的方式管理配置，实现配置的动态变更，大大降低运维成本，让开发同学早点下班。</p>
<p>当然，Nacos 的配置管理，不单单只有上述的那些功能，还有诸如“灰度发布”、“版本管理”、“快速回滚”、“监听查询”、“推送轨迹”、“权限控制”、“敏感配置（如，数据库连接配置）的加密存储”等等，这些有的已经在 Nacos 中开源实现了，有的在 Nacos 配置管理的阿里云免费产品 <a href="https://cn.aliyun.com/product/acm">ACM</a> 中提供了，当然，后续也会慢慢开源到 Nacos 中，敬请期待。</p>
<p>本系列文章，会持续为大家讲述 Nacos 的点点滴滴，不单单讲述 “Nacos 能帮我们解决什么问题？”，还会深入源码分析“Nacos 是如何做到简单而强大的？”。同时，如果小伙们有兴趣的话，我们还会给大家八卦一下 Nacos 的 稗官野史，关于 Nacos 在阿里内部的历史，关于 Nacos 服务端口的寓意等等。总之，一句话：我有故事，也有美酒，君还何求？</p>
</section><footer class="footer-container" data-reactid="29"><div class="footer-body" data-reactid="30"><img src="/img/nacos_gray.png" data-reactid="31"/><div class="cols-container" data-reactid="32"><div class="col col-12" data-reactid="33"><h3 data-reactid="34">愿景</h3><p data-reactid="35">Nacos 通过提供简单易用的动态服务发现、服务配置、服务共享与管理等服务基础设施，帮助用户在云原生时代，在私有云、混合云或者公有云等所有云环境中，更好的构建、交付、管理自己的微服务平台，更快的复用和组合业务服务，更快的交付商业创新的价值，从而为用户赢得市场。</p></div><div class="col col-6" data-reactid="36"><dl data-reactid="37"><dt data-reactid="38">文档</dt><dd data-reactid="39"><a href="/zh-cn/docs/what-is-nacos.html" target="_self" data-reactid="40">概览</a></dd><dd data-reactid="41"><a href="/zh-cn/docs/quick-start.html" target="_self" data-reactid="42">快速开始</a></dd><dd data-reactid="43"><a href="/zh-cn/docs/contributing.html" target="_self" data-reactid="44">开发者指南</a></dd></dl></div><div class="col col-6" data-reactid="45"><dl data-reactid="46"><dt data-reactid="47">资源</dt><dd data-reactid="48"><a href="/zh-cn/community/index.html" target="_self" data-reactid="49">社区</a></dd><dd data-reactid="50"><a href="https://www.aliyun.com/product/acm?source_type=nacos_pc_20181219" target="_self" data-reactid="51">云服务 ACM</a></dd><dd data-reactid="52"><a href="https://www.aliyun.com/product/edas?source_type=nacos_pc_20181219" target="_self" data-reactid="53">云服务 EDAS</a></dd><dd data-reactid="54"><a href="https://www.aliyun.com/product/ahas?source_type=nacos_pc_20190225" target="_self" data-reactid="55">云服务 AHAS</a></dd></dl></div></div><div class="copyright" data-reactid="56"><span data-reactid="57">@ 2018 The Nacos Authors | An Alibaba Middleware (Aliware) Project</span></div></div></footer></div></div>
	<script src="https://f.alicdn.com/react/15.4.1/react-with-addons.min.js"></script>
	<script src="https://f.alicdn.com/react/15.4.1/react-dom.min.js"></script>
	<script>
		window.rootPath = '';
  </script>
	<script src="/build/blogDetail.js"></script>
</body>
</html>